﻿using System;
using System.Collections.Generic;
using System.ServiceModel;
using Informant.UI.SmsService;

namespace Informant.UI
{
	public class ApplicationContext : BindableObject, IContactServiceCallback, ISmsServiceCallback
	{
		#region Fields

		private ContactServiceClient _contactServiceClient;
		private SmsServiceClient _smsServiceClient;
		private Action<Boolean, String> _logOnCallback;
		private Action<Boolean> _logOffCallback;
		private List<Action<IEnumerable<Contact>>> _retrieveAllContactsCallbacks;
		private List<Action<IEnumerable<Group>>> _retrieveAllGroupsCallbacks;
		private List<Action<Int32, Int32, Int32>> _sendSmsProgressCallbacks;
		private List<Action<Boolean>> _sendSmsCallbacks;
		private Boolean _isContactServiceLoggedOn;
		private Boolean _isSmsServiceLoggedOn;
		private String _contactServiceFailureMessage;
		private String _smsServiceFailureMessage;
		private Boolean _contactServiceResponded;
		private Boolean _smsServiceResponded;

		#endregion

		#region Properties

		/// <summary>
		/// Gets a value indicating whether both the Contact and Sms Services are logged on.
		/// </summary>
		/// <value><c>true</c> if [logged on]; otherwise, <c>false</c>.</value>
		public Boolean LoggedOn
		{
			get
			{
				Boolean loggedOn = false;
				if (_contactServiceClient != null && _smsServiceClient != null
					&& _isContactServiceLoggedOn && _isSmsServiceLoggedOn
					&& _contactServiceClient.State == CommunicationState.Opened && _smsServiceClient.State == CommunicationState.Opened)
				{
					loggedOn = true;
				}
				return loggedOn;
			}
		}

		#endregion

		//#region Events

		///// <summary>
		///// Occurs when send SMS progress updated.
		///// </summary>
		//public event EventHandler<SendSmsProgressEventArgs> SendSmsProgressUpdated;
		//private void OnSendSmsProgressUpdated(Int32 sent, Int32 failed, Int32 total)
		//{
		//    if (SendSmsProgressUpdated != null)
		//    {
		//        SendSmsProgressUpdated(this, new SendSmsProgressEventArgs(sent, failed, total));
		//    }
		//}

		//#endregion

		#region Singleton Construction

		private static ApplicationContext _instance;
		/// <summary>
		/// Gets the singleton instance of ApplicationContext.
		/// </summary>
		/// <value>The instance.</value>
		public static ApplicationContext Instance
		{
			get
			{
				if (_instance == null)
				{
					_instance = new ApplicationContext();
				}
				return _instance;
			}
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ApplicationContext"/> class.
		/// </summary>
		private ApplicationContext() { }

		#endregion

		#region Public Methods

		/// <summary>
		/// Logs the user on.
		/// </summary>
		/// <param name="username">The username.</param>
		/// <param name="password">The password.</param>
		/// <returns></returns>
		public void LogOn(String username, String password, Action<Boolean, String> callback)
		{
			InitializeContactService();
			InitializeSmsService();
			_contactServiceResponded = false;
			_smsServiceResponded = false;
			_logOnCallback = callback;

			_contactServiceClient.BeginLogOn(username, password, EndLogOnContactServiceCallback, null);
			_smsServiceClient.BeginLogOn(username, password, EndLogOnSmsServiceCallback, null);
		}
		/// <summary>
		/// Logs the user off.
		/// </summary>
		/// <returns></returns>
		public void LogOff(Action<Boolean> callback)
		{
			_contactServiceResponded = false;
			_smsServiceResponded = false;
			_logOffCallback = callback;

			if (_contactServiceClient != null && _contactServiceClient.State == CommunicationState.Opened)
			{
				_contactServiceClient.BeginLogOff(EndLogOffContactServiceCallback, null);
			}
			else
			{
				ClearContactService();
			}

			if (_smsServiceClient != null && _smsServiceClient.State == CommunicationState.Opened)
			{
				_smsServiceClient.BeginLogOff(EndLogOffSmsServiceCallback, null);
			}
			else
			{
				ClearSmsService();
			}
		}
		/// <summary>
		/// Retrieves the contacts.
		/// </summary>
		/// <returns></returns>
		public void RetrieveContacts(Action<IEnumerable<Contact>> callback)
		{
			_retrieveAllContactsCallbacks.Add(callback);
			_contactServiceClient.RetrieveAllContactsAsync();
		}
		/// <summary>
		/// Retrieves the groups.
		/// </summary>
		/// <returns></returns>
		public void RetrieveGroups(Action<IEnumerable<Group>> callback)
		{
			_retrieveAllGroupsCallbacks.Add(callback);
			_contactServiceClient.RetrieveAllGroupsAsync();
		}
		/// <summary>
		/// Sends the SMS.
		/// </summary>
		/// <param name="recipients">The recipients.</param>
		/// <param name="messageBody">The message body.</param>
		/// <returns></returns>
		/// <exception cref="Informant.ErrorHandling.SmsException"/>
		public void SendSms(Sms sms, Action<Boolean> callback, Action<Int32, Int32, Int32> progressCallback)
		{
			_sendSmsCallbacks.Add(callback);
			_sendSmsProgressCallbacks.Add(progressCallback);
			_smsServiceClient.SendSmsAsync(sms);
		}

		#endregion

		#region Private Methods

		/// <summary>
		/// Initializes the contact service.
		/// </summary>
		/// <param name="isReinitializing">if set to <c>true</c> [is reinitializing].</param>
		private void InitializeContactService()
		{
			_contactServiceClient = new ContactServiceClient(new InstanceContext(this));
			_retrieveAllContactsCallbacks = new List<Action<IEnumerable<Contact>>>();
			_retrieveAllGroupsCallbacks = new List<Action<IEnumerable<Group>>>();
		}
		/// <summary>
		/// Initializes the SMS service.
		/// </summary>
		private void InitializeSmsService()
		{
			_smsServiceClient = new SmsServiceClient(new InstanceContext(this));
			_sendSmsCallbacks = new List<Action<Boolean>>();
			_sendSmsProgressCallbacks = new List<Action<Int32, Int32, Int32>>();
		}
		/// <summary>
		/// Clears the contact service.
		/// </summary>
		private void ClearContactService()
		{
			_contactServiceClient = null;
			_retrieveAllContactsCallbacks = null;
			_retrieveAllGroupsCallbacks = null;
		}
		/// <summary>
		/// Clears the SMS service.
		/// </summary>
		private void ClearSmsService()
		{
			_smsServiceClient = null;
			_sendSmsCallbacks = null;
		}
		/// <summary>
		/// Handles the async callback of both ContactService and SmsService.
		/// </summary>
		private void LogOnHandler()
		{
			if (_contactServiceResponded && _smsServiceResponded)
			{
				if (_logOnCallback != null)
				{
					_logOnCallback(LoggedOn, CreateFailureMessage());
					_logOnCallback = null;
					_contactServiceResponded = false;
					_smsServiceResponded = false;
				}
				PropertyChange("LoggedOn");
			}
		}
		/// <summary>
		/// Logs the off handler.
		/// </summary>
		private void LogOffHandler()
		{
			if (_contactServiceResponded && _smsServiceResponded)
			{
				if (_logOffCallback != null)
				{
					_logOffCallback(LoggedOn);
					_logOffCallback = null;
					_contactServiceResponded = false;
					_smsServiceResponded = false;
				}
			}
		}
		/// <summary>
		/// Creates the failure message.
		/// </summary>
		/// <returns></returns>
		private String CreateFailureMessage()
		{
			//FUTURE: For AccountLocked errors provide web access to link.
			String failureMessage = null;
			if (!LoggedOn)
			{
				if (_contactServiceFailureMessage == _smsServiceFailureMessage)
				{
					failureMessage = _contactServiceFailureMessage;
				}
				else
				{
					failureMessage = String.Format(
						String.Concat("Error 1: {0}", Environment.NewLine, "Error 2: {1}"),
						_contactServiceFailureMessage,
						_smsServiceFailureMessage);
				}
			}
			return failureMessage;
		}

		#endregion

		#region Callback Methods

		/// <summary>
		/// Callback for LogOnContactService.
		/// </summary>
		/// <param name="loggedOn">if set to <c>true</c> [logged on].</param>
		public void LogOnContactServiceCallback(Boolean loggedOn)
		{
			_contactServiceResponded = true;
			_isContactServiceLoggedOn = loggedOn;
			LogOnHandler();
		}
		/// <summary>
		/// Ends the log on contact service callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndLogOnContactServiceCallback(IAsyncResult result)
		{
			try
			{
				_contactServiceClient.EndLogOn(result);
			}
			catch (FaultException<AuthorizationFault> ex)
			{
				_contactServiceFailureMessage = ex.Detail.Message;
				LogOnContactServiceCallback(false);
			}
			catch (FaultException ex)
			{
				LogOnContactServiceCallback(false);
			}
		}
		/// <summary>
		/// Callback for LogOffContactService.
		/// </summary>
		/// <param name="loggedOff">if set to <c>true</c> [logged off].</param>
		public void LogOffContactServiceCallback(Boolean loggedOff)
		{
			_contactServiceResponded = true;
			LogOffHandler();
		}
		/// <summary>
		/// Ends the log off contact service callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndLogOffContactServiceCallback(IAsyncResult result)
		{
			try
			{
				_contactServiceClient.EndLogOff(result);
			}
			catch (FaultException<AuthorizationFault> ex)
			{
				LogOffContactServiceCallback(false);
			}
			catch (FaultException ex)
			{
				LogOffContactServiceCallback(false);
			}
			ClearContactService();
		}
		/// <summary>
		/// Callback for RetrieveAllContacts.
		/// </summary>
		/// <param name="contacts">The contacts.</param>
		public void RetrieveAllContactsCallback(List<Contact> contacts)
		{
			while (_retrieveAllContactsCallbacks.Count > 0)
			{
				_retrieveAllContactsCallbacks[0](contacts);
				_retrieveAllContactsCallbacks.RemoveAt(0);
			}
		}
		/// <summary>
		/// Ends the retrieve all contacts callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndRetrieveAllContactsCallback(IAsyncResult result)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Callback for RetrieveAllGroups.
		/// </summary>
		/// <param name="groups">The groups.</param>
		public void RetrieveAllGroupsCallback(List<Group> groups)
		{
			while (_retrieveAllGroupsCallbacks.Count > 0)
			{
				_retrieveAllGroupsCallbacks[0](groups);
				_retrieveAllGroupsCallbacks.RemoveAt(0);
			}
		}
		/// <summary>
		/// Ends the retrieve all groups callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndRetrieveAllGroupsCallback(IAsyncResult result)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Callback for LogOnSmsService.
		/// </summary>
		/// <param name="loggedOn">if set to <c>true</c> [logged on].</param>
		public void LogOnSmsServiceCallback(Boolean loggedOn)
		{
			_smsServiceResponded = true;
			_isSmsServiceLoggedOn = loggedOn;
			LogOnHandler();
		}
		/// <summary>
		/// Ends the log on SMS service callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndLogOnSmsServiceCallback(IAsyncResult result)
		{
			try
			{
				_smsServiceClient.EndLogOn(result);
			}
			catch (FaultException<AuthorizationFault> ex)
			{
				_smsServiceFailureMessage = ex.Detail.Message;
				LogOnSmsServiceCallback(false);
			}
			catch (FaultException ex)
			{
				LogOnSmsServiceCallback(false);
			}
		}
		/// <summary>
		/// Callback for LogOffSmsService.
		/// </summary>
		/// <param name="loggedOff">if set to <c>true</c> [logged off].</param>
		public void LogOffSmsServiceCallback(Boolean loggedOff)
		{
			_smsServiceResponded = true;
			LogOffHandler();
		}
		/// <summary>
		/// Ends the log off SMS service callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndLogOffSmsServiceCallback(IAsyncResult result)
		{
			try
			{
				_smsServiceClient.EndLogOff(result);
			}
			catch (FaultException<AuthorizationFault> ex)
			{
				LogOffSmsServiceCallback(false);
			}
			catch (FaultException ex)
			{
				LogOffSmsServiceCallback(false);
			}
			ClearSmsService();
		}
		/// <summary>
		/// Callback for DeleteSms.
		/// </summary>
		/// <param name="smsId">The SMS id.</param>
		/// <param name="deleted">if set to <c>true</c> [deleted].</param>
		public void DeleteSmsCallback(String smsId, Boolean deleted)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Ends the delete SMS callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndDeleteSmsCallback(IAsyncResult result)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Callback for MarkReadSms.
		/// </summary>
		/// <param name="smsId">The SMS id.</param>
		/// <param name="isRead">if set to <c>true</c> [is read].</param>
		public void MarkReadSmsCallback(String smsId, Boolean isRead)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Ends the mark read SMS callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndMarkReadSmsCallback(IAsyncResult result)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Callback for RetrieveAllSms.
		/// </summary>
		/// <param name="allSms">All SMS.</param>
		public void RetrieveAllSmsCallback(List<Sms> allSms)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Ends the retrieve all SMS callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndRetrieveAllSmsCallback(IAsyncResult result)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Callback for SendSms.
		/// </summary>
		/// <param name="successful">if set to <c>true</c> [successful].</param>
		public void SendSmsCallback(Boolean successful)
		{
			while (_sendSmsCallbacks.Count > 0)
			{
				_sendSmsCallbacks[0](successful);
				_sendSmsCallbacks.RemoveAt(0);
			}
		}
		/// <summary>
		/// Ends the send SMS callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndSendSmsCallback(IAsyncResult result)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Callback for SendSmsProgress.
		/// </summary>
		/// <param name="sent">The sent.</param>
		/// <param name="failed">The failed.</param>
		/// <param name="total">The total.</param>
		public void SendSmsProgressCallback(Int32 sent, Int32 failed, Int32 total)
		{
			foreach (Action<Int32,Int32,Int32> progressCallback in _sendSmsProgressCallbacks)
			{
				progressCallback(sent, failed, total);
			}
		}

		#region Not In Use

		public IAsyncResult BeginLogOnContactServiceCallback(Boolean loggedOn, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginLogOffContactServiceCallback(Boolean loggedOff, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginRetrieveAllContactsCallback(List<Contact> contacts, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginRetrieveAllGroupsCallback(List<Group> groups, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginLogOnSmsServiceCallback(Boolean loggedOn, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginLogOffSmsServiceCallback(Boolean loggedOff, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginDeleteSmsCallback(String smsId, Boolean deleted, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginMarkReadSmsCallback(String smsId, Boolean isRead, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginRetrieveAllSmsCallback(List<Sms> allSms, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginSendSmsCallback(Boolean successful, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginSendSmsProgressCallback(Int32 sent, Int32 failed, Int32 total, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public void EndSendSmsProgressCallback(IAsyncResult result)
		{
			throw new NotImplementedException();
		}

		#endregion

		#endregion
	}
}
